using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OpenCvSharp
{
    public static class CvExtensions
    {
        public static bool IsValid(this DisposableObject obj)
        {
            return obj != null && !obj.IsDisposed;
        }
        public static bool IsValid(this DisposableCvObject obj)
        {
            return obj != null && !obj.IsDisposed && obj.CvPtr != IntPtr.Zero;
        }
        public static bool IsValid(this Mat obj)
        {
            return obj != null && !obj.IsDisposed && obj.CvPtr != IntPtr.Zero && !obj.Empty();
        }

        public static void IfDispose(this DisposableObject obj)
        {
            if (IsValid(obj))
            {
                obj.Dispose();
            }
        }
        public static void IfDispose(this DisposableCvObject obj)
        {
            if (IsValid(obj))
            {
                obj.Dispose();
            }
        }
        public static void IfDispose(this Mat obj)
        {
            if (IsValid(obj))
            {
                obj.Dispose();
            }
        }

        public static string ToCsvString(this Mat mat)//测试使用的方法，需要删除，期待有缘人可以写个VS插件，在调试监视时可以直接看到矩阵内的数据、或者图像
        {
            MatType type = mat.Type();
           
            switch (type.Depth)
            {
                case MatType.CV_8U:
                    switch (type.Channels)
                    {
                        case 1:
                            using (Mat<byte> src = new Mat<byte>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 2:
                            using (Mat<Vec2b> src = new Mat<Vec2b>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 3:
                            using (Mat<Vec3b> src = new Mat<Vec3b>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 4:
                            using (Mat<Vec4b> src = new Mat<Vec4b>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case MatType.CV_8S:
                    using (Mat mat16S = new Mat())
                    {
                        mat.AssignTo(mat16S, MatType.CV_16SC(type.Channels));
                        switch (type.Channels)
                        {
                            case 1:
                                using (Mat<short> src = new Mat<short>(mat16S))
                                {
                                    var arr = src.ToRectangularArray();
                                   return ToCsvString(arr);
                                }
                                break;
                            case 2:
                                using (Mat<Vec2s> src = new Mat<Vec2s>(mat16S))
                                {
                                    var arr = src.ToRectangularArray();
                                   return ToCsvString(arr);
                                }
                                break;
                            case 3:
                                using (Mat<Vec3s> src = new Mat<Vec3s>(mat16S))
                                {
                                    var arr = src.ToRectangularArray();
                                   return ToCsvString(arr);
                                }
                                break;
                            case 4:
                                using (Mat<Vec4s> src = new Mat<Vec4s>(mat16S))
                                {
                                    var arr = src.ToRectangularArray();
                                   return ToCsvString(arr);
                                }
                                break;
                            default:
                                break;
                        }
                    }            
                    break;
                case MatType.CV_16U:
                    switch (type.Channels)
                    {
                        case 1:
                            using (Mat<ushort> src = new Mat<ushort>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 2:
                            using (Mat<Vec2w> src = new Mat<Vec2w>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 3:
                            using (Mat<Vec3w> src = new Mat<Vec3w>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 4:
                            using (Mat<Vec4w> src = new Mat<Vec4w>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case MatType.CV_16S:
                    switch (type.Channels)
                    {
                        case 1:
                            using (Mat<short> src = new Mat<short>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 2:
                            using (Mat<Vec2s> src = new Mat<Vec2s>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 3:
                            using (Mat<Vec3s> src = new Mat<Vec3s>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 4:
                            using (Mat<Vec4s> src = new Mat<Vec4s>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case MatType.CV_32S:
                    switch (type.Channels)
                    {
                        case 1:
                            using (Mat<int> src = new Mat<int>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 2:
                            using (Mat<Vec2i> src = new Mat<Vec2i>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 3:
                            using (Mat<Vec3i> src = new Mat<Vec3i>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 4:
                            using (Mat<Vec4i> src = new Mat<Vec4i>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case MatType.CV_32F:
                    switch (type.Channels)
                    {
                        case 1:
                            using (Mat<float> src = new Mat<float>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 2:
                            using (Mat<Vec2f> src = new Mat<Vec2f>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 3:
                            using (Mat<Vec3f> src = new Mat<Vec3f>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 4:
                            using (Mat<Vec4f> src = new Mat<Vec4f>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case MatType.CV_64F:
                    switch (type.Channels)
                    {
                        case 1:
                            using (Mat<double> src = new Mat<double>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 2:
                            using (Mat<Vec2d> src = new Mat<Vec2d>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 3:
                            using (Mat<Vec3d> src = new Mat<Vec3d>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        case 4:
                            using (Mat<Vec4d> src = new Mat<Vec4d>(mat))
                            {
                                var arr = src.ToRectangularArray();
                               return ToCsvString(arr);
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case MatType.CV_USRTYPE1:
                default:
                    using (Mat mat64F = new Mat())
                    {
                        mat.AssignTo(mat64F, MatType.CV_64FC(type.Channels));
                        switch (type.Channels)
                        {
                            case 1:
                                using (Mat<double> src = new Mat<double>(mat64F))
                                {
                                    var arr = src.ToRectangularArray();
                                   return ToCsvString(arr);
                                }
                                break;
                            case 2:
                                using (Mat<Vec2d> src = new Mat<Vec2d>(mat64F))
                                {
                                    var arr = src.ToRectangularArray();
                                   return ToCsvString(arr);
                                }
                                break;
                            case 3:
                                using (Mat<Vec3d> src = new Mat<Vec3d>(mat64F))
                                {
                                    var arr = src.ToRectangularArray();
                                   return ToCsvString(arr);
                                }
                                break;
                            case 4:
                                using (Mat<Vec4d> src = new Mat<Vec4d>(mat64F))
                                {
                                    var arr = src.ToRectangularArray();
                                   return ToCsvString(arr);
                                }
                                break;
                            default:
                                break;
                        }
                    }
                    break;
            }
            return string.Empty;
        }
        private static string ToCsvString<T>(T[,] arr)
        {
            StringBuilder sb = new StringBuilder();
            if(typeof(IVec).IsAssignableFrom(typeof(T)))
            {
                string txt;
                for (int r = 0, rc = arr.GetLength(0); r < rc; r++)
                {
                    for (int c = 0, cc = arr.GetLength(1); c < cc; c++)
                    {
                        txt = arr[r, c].ToString();
                        txt = txt.Substring(7, txt.Length - 8);
                        txt = txt.Replace(", ", "|");
                        sb.Append(txt);
                        sb.Append(",");
                    }
                    sb.Remove(sb.Length - 1, 1);
                    sb.AppendLine();
                }
            }
            else
            {
                for (int r = 0, rc = arr.GetLength(0); r < rc; r++)
                {
                    for (int c = 0, cc = arr.GetLength(1); c < cc; c++)
                    {
                        sb.Append(arr[r, c]);
                        sb.Append(",");
                    }
                    sb.Remove(sb.Length - 1, 1);
                    sb.AppendLine();
                }
            }
            return sb.ToString();
        }
        
        public static string ImShow(this Mat image, string title, bool waitKey = true, WindowFlags flags = WindowFlags.GuiExpanded)
        {
            string winName = "ImShow" + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            Cv2.NamedWindow(winName, flags);
            Cv2.SetWindowTitle(winName, title);
            Cv2.ImShow(winName, image);
            if (waitKey)
            {
                Cv2.WaitKey();
                Cv2.DestroyWindow(winName);
            }
            return winName;
        }
    }
}
